package transformVolOne;

/**
 * Generates the clauses to be passed to a SAT-Solver for satisfiability testing.
 * takes in two values, N = row size of grid and M = column size of grid
 * forming a N x M grid.
 * @author MacDerson Louis
 * @param <Box>
 *
 */
public class ClauseEngine{

	private transformVolOne.Box[] rectangle;
	private int n; //must always represent the row value
	private int m; //must always represent the column value
	private int currentN;
	private int currentM;
	private int numberOfIterations;
	private int maxIterations;
	private int i;
	private int j;

	/**
	 * basic constructor
	 */
	public ClauseEngine(){		
	}

	/**
	 * main constructor, initializes rectangle array to a given size, and sets
	 * dimensions
	 * @param n - row
	 * @param m - column
	 */
	public ClauseEngine(int n, int m){
		rectangle = new transformVolOne.Box[(n * m)];
		this.n = n;
		this.m = m;
		arrangeGrid();
		currentN = 2; //current row size
		currentM = 2; //current column size
		numberOfIterations = 0;
		maxIterations = 0;
		//needed to calculate max iterations for each rectangle dimension
		i = 0;
		j = 0; 
	}	

	/**
	 * set row and column lengths
	 * @param n - row
	 * @param m - column
	 */
	public void setDimensions(int n, int m){
		this.n = n;
		this.m = m;
	}

	/**
	 * before calling this method, setDimensions() must be called, if class was 
	 * initialized using empty constructor.
	 */
	public void initializeRectangle(){
		rectangle = new transformVolOne.Box[n * m];
	}

	/**
	 * fills rectangle array with n * m Boxes, initialized to no color(0)
	 */
	public void fillRectangles(){
		for(int i = 0; i < rectangle.length; i++){
			rectangle[i] = new transformVolOne.Box();
			rectangle[i].setIndex(i);
		}
	}

	/**
	 * given the row and column number of a two-dimensional array or grid, 
	 * return the index number of a one-dimensional array, where the value 
	 * contained at that intersection would reside.
	 * @param row - row number 
	 * @param column - column number
	 * @return index number of array for that row, column intersection
	 */
	public int getIndex(int row, int column){
		return ((row * m) + column);
	}

	/**
	 * Finds next triangle within the grid, used with a loop, can be used
	 * to iterate through the entire grid.
	 * @param rec
	 * @return - next rectangle
	 */
	public int[] getNextRectangle(int[] rec){
		
		getNumberOfIterations();//calls a private method to check the number
		//iterations needed to count all rectangles of a certain size.
		
		if(numberOfIterations == (maxIterations -1)){
			if(currentM < m){
				currentM++;
				rec = resetRectangle(rec);
				return rec;
			}else if(currentN < n){
				currentN++;
				currentM = currentN;
				rec = resetRectangle(rec);
				return rec;
			}
		}		
		return next(rec);
	}
	
	/**
	 * calculates the number of iterations needed for a rectangle of a 
	 * particular size.
	 */
	private void getNumberOfIterations(){
		i = currentM - 2; 
		j = currentN;	
		//if current rectangle h x w is a square e.g 2x2, 3x3, 4x4, etc.
		if((currentN == currentM) && currentN == 2){ 
			//this is the max number of rectangles of that size
			maxIterations = ((n - 1) * (m - 1));
		}else if((currentN == currentM) && currentN > 2)
			maxIterations = ((n - (currentN - 1)) * (m - (currentM - 1)));
		else{//otherwise
			//this is the max number of rectangles
			//of that size.
			maxIterations = ((n - i) * (m - j)) + ((n - (i + 1)) * (m - 1));
		}
	}
	
	/**
	 * returns next rectangle in the grid
	 * @param rec
	 * @return
	 */
	private int[] next(int[] rec){
		int length = rec.length;		
		if(rec[0] == 0 && rec[3] >= (rectangle.length - 1)){			
			for(int i = 0; i < length; i++){
				rec[i] = 0;					
			}numberOfIterations = 0;
		}
		else if((rec[3] == (rectangle.length - 1)) && numberOfIterations < maxIterations){
			rec = flipVertical(rec);
			numberOfIterations++;
		}
		else if((rec[1] + 1) % m == 0){// && rec[3] % m  == 0){
			for(int i = 0; i < length; i++){
				rec[i] = rec[i] + currentM;				
			}numberOfIterations++;
		}
		else{
			for(int i = 0; i < length; i++){
				rec[i] = rec[i] + 1;				
			}numberOfIterations++;
		}
		return rec;
	}
	
	private int[] flipVertical(int[] rec){
		rec[0] = 0;
		rec[1] = getIndex(0, currentN - 1);
		rec[2] = getIndex(currentM - 1, 0);
		rec[3] = getIndex(currentM - 1, currentN - 1);
		
		return rec;		
	}
	
	/**
	 * resets the rectangle to the top left corner of the grid, with the 
	 * new dimension.
	 * @param rec - array containing index of rectangle corners
	 * @return rec - array containing index of rectangle corners
	 */
	private int[] resetRectangle(int[] rec){
		rec[0] = 0;
		rec[1] = currentM - 1;
		rec[2] = getIndex(currentN - 1, 0);
		rec[3] = getIndex(currentN - 1, currentM - 1);
		numberOfIterations = 0;
		
		return rec;
	}

	/**
	 * returns the first rectangle within the grid located
	 * at the top left corner of the grid, which are index
	 * (0, 1, n, n+1).
	 */
	public int[] getFirstRectangle(){
		int[] indices = new int[4];
		
		indices[0] = 0;
		indices[1] = 1;
		indices[2] = m;
		indices[3] = m+1;
		
		return indices;
	}

	/**
	 * returns the number of rectangles of size 2x2 and larger, in a n x m grid
	 * @return - the total number of rectangles within the grid.
	 */
	public int getNumRect(){
		int num = 0;
		num = (int)(((n*(n - 1))/2) * ((m*(m - 1))/2));

		return num;
	}

	/**
	 * returns the rectangle array
	 * @return - rectangle
	 */
	public Box[] getRectangle(){
		return rectangle;
	}
	
	/**
	 * the equations used in this program to calculate the number of rectangles
	 * of each size only works when the column m is greater than the row n, 
	 * therefore, if the given n is greater than m we swap them...the dimensions 
	 * still remain the same.
	 */
	private void arrangeGrid(){
		if(n > m)
			swap();
	}
	
	/**
	 * swap n and m
	 */
	private void swap(){
		int temp = n;
		n = m;
		m = temp;
	}
	
	/**
	 * sets the number of iterations already passed. Used for providing info to the engine
	 * for collecting clauses
	 * @param num
	 */
	public void setNumberOfIterations(int num){
		numberOfIterations = num;
	}
	
	/**
	 * used to reset the currentN value after the removal of monochromatic rectangles, for the use
	 * of the clause generator.
	 * @param currN
	 */
	public void resetCurrentN(int currN){
		currentN = currN;
	}
	
	/**
	 * used to reset the currentM value after the removal of monochromatic rectangles, for the use
	 * of the clause generator.
	 * @param currM
	 */
	public void resetCurrentM(int currM){
		currentM = currM;
	}
	
	public void resetMaxIterations(){
		maxIterations = 0;
	}
	
	
}
